#pragma semicolon 1 // Force strict semicolon mode.

#define REQUIRE_EXTENSIONS
#include <sourcemod>
#include <sdktools>
#include <tf2_stocks>
#include <psycore>
#include <tfx>
#undef REQUIRE_EXTENSIONS
#include <sdkhooks>

#define PLUGIN_NAME              "psyFX - Sickness'"
#define PLUGIN_AUTHOR            "Thrawn"
#define PLUGIN_VERSION           "1.0.0.0"
#define PLUGIN_CONTACT           "http://aaa.einfachonline.net/"

#define SOUND_QUAKE				"ambient/atmosphere/terrain_rumble1.wav"
#define SOUND_DANCEFEVER 		"vo/engineer_cheers02.wav"
#define SOUND_MELEEONLY 		"player/taunt_bell.wav"
#define SOUND_SWITCHEROO		"weapons/teleporter_send.wav"

#define PLAYER_TAUNTING (1 << 7)

new g_iEffectNum[8] = {-1,...};
new Handle:g_clientEffectTimer[MAXPLAYERS + 1][2];

new bool:g_bHasQuake[MAXPLAYERS + 1] = {false, ...};
new bool:g_bHasDance[MAXPLAYERS + 1] = {false, ...};
new bool:g_bHasMelee[MAXPLAYERS + 1] = {false, ...};
new bool:g_bHasOvertime[MAXPLAYERS + 1] = {false, ...};

new g_clientTaunts[MAXPLAYERS + 1] = {0,...};

new String:g_sOtSounds[4][] = {
	"vo/announcer_overtime.wav",
	"vo/announcer_overtime2.wav",
	"vo/announcer_overtime3.wav",
	"vo/announcer_overtime4.wav"
};

public Plugin:myinfo = {
    name        = PLUGIN_NAME,
    author      = PLUGIN_AUTHOR,
    description = PLUGIN_NAME,
    version     = PLUGIN_VERSION,
    url         = PLUGIN_CONTACT
};

public OnPluginStart() {
	HookEvent("player_hurt", Event_PlayerHurt);
}

public OnMapStart()
{
	//PrecacheModel(MODEL_CONE, true);
	PrecacheSound(SOUND_QUAKE, true);
	PrecacheSound(SOUND_DANCEFEVER, true);	
	PrecacheSound(SOUND_MELEEONLY, true);	
	PrecacheSound(SOUND_SWITCHEROO, true);
	
	for(new i = 0; i < sizeof(g_sOtSounds); i++) {
		PrecacheSound(g_sOtSounds[i], true);
	}
}

public OnAllPluginsLoaded() {
	if(LibraryExists("psycore")) {
		g_iEffectNum[0] = psyRTD_RegisterEffect(psyRTDEffectType_Bad,"Earthquake Pills","Warning: Does not work on Road Runners", EffectQuake);
		g_iEffectNum[1] = psyRTD_RegisterEffect(psyRTDEffectType_Bad,"Dance Fever","You've caught dance fever! It's contagious...", EffectDanceFever);
		g_iEffectNum[2] = psyRTD_RegisterEffect(psyRTDEffectType_Bad,"Overtime","Overtime! Overtime! Overtime!", EffectOvertime);
		g_iEffectNum[3] = psyRTD_RegisterEffect(psyRTDEffectType_Bad,"Switcheroo","You are now somewhere else!", EffectSwitcheroo);
		
		if(GetExtensionFileStatus("sdkhooks.ext") == 1) {
			g_iEffectNum[4] = psyRTD_RegisterEffect(psyRTDEffectType_Bad,"Melee Only","Melee Only! It's contagious...", EffectMeleeOnly);
		} else {
			LogMessage("SDKHooks not found. Melee Only not loaded");
		}
	}		
}

public OnPluginEnd() {
	psyRTD_UnregisterEffect(g_iEffectNum[0]);
	psyRTD_UnregisterEffect(g_iEffectNum[1]);
	psyRTD_UnregisterEffect(g_iEffectNum[2]);
	psyRTD_UnregisterEffect(g_iEffectNum[3]);
	if(g_iEffectNum[4] != -1)
		psyRTD_UnregisterEffect(g_iEffectNum[4]);

}

public psyRTD_DisableEffect(client,effectId) {
	for(new i = 0; i < sizeof(g_iEffectNum); i++) {
		if(effectId == g_iEffectNum[i]) {
			if(g_clientEffectTimer[client][0] != INVALID_HANDLE)
				TriggerTimer(g_clientEffectTimer[client][0]);

			if(g_clientEffectTimer[client][1] != INVALID_HANDLE)
				TriggerTimer(g_clientEffectTimer[client][1]);
		}
	}
}

public Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast)
{
	//if(true) {
		new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
		new victim = GetClientOfUserId(GetEventInt(event,"userid"));

		if (attacker) {
			if (attacker != victim) {			
				// Dance Fever
				if (g_bHasDance[victim] && !g_bHasDance[attacker]) {
					EffectDanceFever(attacker);
				}
				
				if (!g_bHasMelee[victim] && g_bHasMelee[attacker]) {
					EffectMeleeOnly(victim);
				}
			}
		}
	//}
}

public Action:OnWeaponSwitch(client, weapon)
{
	//FIXME condition g_bEnable
	if(true && g_bHasMelee[client]) {	
		new defIdx = GetEntProp(weapon, Prop_Send, "m_iItemDefinitionIndex");

		if( defIdx < 9 || defIdx == 44 || defIdx == 128 || defIdx == 38 || defIdx == 132 || defIdx == 43 || defIdx == 37)
			return Plugin_Continue;
		else
			return Plugin_Handled;
	}
    
	return Plugin_Continue;
}

public psyRTDAction:EffectOvertime(client) {
	g_bHasOvertime[client] = true;
	
	g_clientEffectTimer[client][0] = CreateTimer(20.0, Timer_OTOver, client);
	g_clientEffectTimer[client][1] = CreateTimer(1.2, Timer_OTNag, client, TIMER_REPEAT);
	
	EmitSoundClient(g_sOtSounds[GetRandomInt(0,3)], client);
	
	return psyRTD_Continue;
}

public Action:Timer_OTNag(Handle:timer, any:client) {
	if (!g_bHasOvertime[client]) {
		return Plugin_Stop;
	} else {
		EmitSoundClient(g_sOtSounds[GetRandomInt(0,3)], client);
		
		return Plugin_Continue;
	}
}

public Action:Timer_OTOver(Handle:timer, any:client) {
	g_bHasOvertime[client] = false;
	g_clientEffectTimer[client][0] = INVALID_HANDLE;
	g_clientEffectTimer[client][1] = INVALID_HANDLE;
	
	if (client && IsClientInGame(client) && !IsClientInKickQueue(client)) {
		psyRTD_EffectIsNowDisabled(client,g_iEffectNum[2]);
	}	
}

public psyRTDAction:EffectMeleeOnly(client) {
	SDKHook(client, SDKHook_WeaponSwitch, OnWeaponSwitch);

	g_bHasMelee[client] = true;

	g_clientEffectTimer[client][0] = CreateTimer(10.0, Timer_MeleeOnly, client);
	
	new weapon = GetPlayerWeaponSlot(client, 2);
	if (weapon > -1)
	{
		SetEntPropEnt(client, Prop_Send, "m_hActiveWeapon", weapon);
	}	
	
	EmitSoundClient(SOUND_MELEEONLY, client);
	
	return psyRTD_Continue;
}

public Action:Timer_MeleeOnly(Handle:timer, any:client) {
	g_bHasMelee[client] = false;
	g_clientEffectTimer[client][0] = INVALID_HANDLE;
	
	SDKUnhook(client, SDKHook_WeaponSwitch, OnWeaponSwitch);
	
	if (client && IsClientInGame(client) && !IsClientInKickQueue(client)) {
		psyRTD_EffectIsNowDisabled(client,g_iEffectNum[4]);
	}
}

public psyRTDAction:EffectSwitcheroo(client) {	
	
	//find random player
	new Players[MAXPLAYERS+1];
	new playerCnt = 0;
	for(new i = 1; i <= MaxClients; i++)
    {
    	if(IsClientInGame(i) && IsPlayerAlive(i) && i != client) {    		
    		Players[playerCnt] = i;
    		playerCnt++;
    	}
	}

	if(playerCnt > 0) {
		new victim = Players[GetRandomInt(0,playerCnt)];
	
		new Float:victimPos[3];
		new Float:victimAng[3];
		new Float:victimVel[3];
		GetClientAbsOrigin(victim, victimPos);
		GetClientAbsAngles(victim, victimAng);
		GetEntPropVector(victim, Prop_Data, "m_vecVelocity", victimVel);
		
		new Float:clientPos[3];
		new Float:clientAng[3];
		new Float:clientVel[3];
		GetClientAbsOrigin(client, clientPos);
		GetClientAbsAngles(client, clientAng);
		GetEntPropVector(client, Prop_Data, "m_vecVelocity", clientVel);
		
		TeleportEntity(client, victimPos, victimAng, victimVel);
		TeleportEntity(victim, clientPos, clientAng, clientVel);
		
		EmitSoundClient(SOUND_SWITCHEROO, client);
		PrintToChat(client, "You have been switched with %N", victim);
		EmitSoundClient(SOUND_SWITCHEROO, victim);
		PrintToChat(victim, "You have been switched with %N", client);
		
		SetHudTextParams(-1.0, 0.4, 5.0, 255, 255, 255, 255);		
		ShowHudText(victim, 3, "%s\n\n\n\n\n%s", "Switcheroo!", "Someone else teleported you!");		
	} else {
		LogMessage("not enough players!");
		return psyRTD_Reroll;
	}
	
	return psyRTD_Continue;
}


public psyRTDAction:EffectDanceFever(client)
{
	g_bHasDance[client] = true;
	
	// Note: Dance Fever no longer has a main timer. Its duration is until the client 
	//       has been forced to taunt twice.

	// Number of times they've taunted
	g_clientTaunts[client] = 0;

	g_clientEffectTimer[client][1] = CreateTimer(0.1, Timer_Dance, client, TIMER_REPEAT);
	
	if (GetEntityFlags(client) & FL_ONGROUND) {		
		FakeClientCommand(client, "taunt");
		g_clientTaunts[client]++;
	}
	
	EmitSoundClient(SOUND_DANCEFEVER, client);
	return psyRTD_Continue;
}

public Action:Timer_Dance(Handle:timer, any:client)
{
	if (!g_bHasDance[client]) {
		return Plugin_Stop;
	}
	
	if (client && IsClientInGame(client) && !IsClientInKickQueue(client)) {
		new cond = GetEntProp(client, Prop_Send, "m_nPlayerCond");
		
		if (!(cond & PLAYER_TAUNTING)) {
			if (g_clientTaunts[client] >= 2) {
				// End effect
				g_bHasDance[client] = false;
				g_clientEffectTimer[client][1] = INVALID_HANDLE;
				
				psyRTD_EffectIsNowDisabled(client,g_iEffectNum[1]);
				
				return Plugin_Stop;
			} else if (GetEntityFlags(client) & FL_ONGROUND) {
				FakeClientCommand(client, "taunt");
				g_clientTaunts[client]++;
			}
		}
	} else {
		g_bHasDance[client] = false;
		g_clientEffectTimer[client][1] = INVALID_HANDLE;
		
		return Plugin_Stop;
	}
	
	return Plugin_Continue;
}


/* EffectQuake()
**
** Effect: Shakes the screen for awhile.
** ------------------------------------------------------------------------- */
public psyRTDAction:EffectQuake(client)
{
	g_bHasQuake[client] = true;
	g_clientEffectTimer[client][0] = CreateTimer(15.0, Timer_Quake, client);
	g_clientEffectTimer[client][1] = CreateTimer(0.25, Timer_QuakeShake, client, TIMER_REPEAT);
	
	Shake(client);
	
	EmitSoundClient(SOUND_QUAKE, client);
	return psyRTD_Continue;
}

/* Timer_QuakeShake()
**
** Shakes the screen periodically.
** ------------------------------------------------------------------------- */
public Action:Timer_QuakeShake(Handle:timer, any:client)
{
	if (!g_bHasQuake[client]) {
		return Plugin_Stop;
	} else {
		Shake(client);
		
		return Plugin_Continue;
	}
}

/* Timer_Quake()
**
** Timer for Quake effect.
** ------------------------------------------------------------------------- */
public Action:Timer_Quake(Handle:timer, any:client)
{
	if (g_clientEffectTimer[client][1] != INVALID_HANDLE) {
		KillTimer(g_clientEffectTimer[client][1]);
		g_clientEffectTimer[client][1] = INVALID_HANDLE;
	}

	g_bHasQuake[client] = false;
	g_clientEffectTimer[client][0] = INVALID_HANDLE;
	
	if (client && IsClientInGame(client) && !IsClientInKickQueue(client)) {
		psyRTD_EffectIsNowDisabled(client,g_iEffectNum[0]);
	}
}

/* Shake()
**
** Shakes the client's screen.
** ------------------------------------------------------------------------- */
stock Shake(client)
{	
	new flags = GetCommandFlags("shake") & (~FCVAR_CHEAT);
	SetCommandFlags("shake", flags);

	FakeClientCommand(client, "shake");
	
	// Timer not needed because we're using FakeClientCommand
	flags = GetCommandFlags("shake") | (FCVAR_CHEAT);
	SetCommandFlags("shake", flags);
}